package cn.edu.bit.ss.parser;

import java.util.ArrayList;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.htmlparser.beans.StringBean;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.ParserException;
import org.htmlparser.Parser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import cn.edu.bit.dto.Source;
import cn.edu.bit.dto.WebPage;
import cn.edu.bit.ss.SpiderMan;
import cn.edu.bit.ss.dao.BaseDAO;
import cn.edu.bit.ss.util.Queue;


@Component
@Scope("prototype")
public class DefaultParser  extends Thread{
	@Autowired
	@Qualifier("parserSleepTime")
	private Integer sleepTime;
	@Autowired
	@Qualifier("urlDepth")
	private Integer urlDepth;
	@Autowired
	@Qualifier("urlPool")
	private Queue<String> urlPool;
	@Autowired
	@Qualifier("sourcePool")
	private Queue<Source> sourcePool;
	private Source source;
	@Autowired
	@Qualifier("dao")
	private BaseDAO dao;
	private static final Log logger = LogFactory.getLog(DefaultParser.class);


	public Integer getSleepTime() {
		return sleepTime;
	}
	public void setSleepTime(Integer sleepTime) {
		this.sleepTime = sleepTime;
	}
	public Integer getUrlDepth() {
		return urlDepth;
	}
	public void setUrlDepth(Integer urlDepth) {
		this.urlDepth = urlDepth;
	}
	public DefaultParser(Queue<String> urlPool,
			Queue<Source> sourcePool, BaseDAO dao) {
		super();
		this.urlPool = urlPool;
		this.sourcePool = sourcePool;
		this.dao=dao;
	}
	public DefaultParser(){}
	
	@Override
	public void run() {
		while(SpiderMan.isWorking()){
			try{				
				fetchSource();				
				WebPage webPage = new WebPage(source.getUrl(),getTitle(source),getContent(source));
				saveToDb(webPage);	
					fillWithUrls(formAbsoluteUrl(getLinks()));
			}catch (Exception e){
				logger.error("SomeError occurred, abort this while, continuing...");
				continue;
			}
		}
		logger.info("Stop working.");
		dao.dispose();
		logger.info("Dispose databse connection.");
	}

	public void fetchSource(){
		synchronized (sourcePool) {
			while(SpiderMan.isWorking()&&sourcePool.isEmpty()){
				try {
					sourcePool.wait();
				} catch (InterruptedException e) {
					logger.warn("Waiting interrupted.");
				}
			}
			source=sourcePool.getNext();
		}
	}
	
	private boolean putUrl(String url){
		synchronized (urlPool) {
			while(urlPool.isFull()){
				return false;
			}
			if(countUrlDepth(url)>this.urlDepth){
				return true;
			}
			urlPool.add(url);			
			urlPool.notifyAll();
		}
		return true;
	}
	public void fillWithUrls(ArrayList<String> arr){
		for(String str : arr){
			if(str==null||str.trim().equals("")||dao.checkIfExisted(new WebPage(str,null,null))){
				continue;
			} 
			if(!putUrl(str)){
				try {
					sleep(sleepTime);
				} catch (InterruptedException e) {
					logger.warn("Sleeping interrupted.");
				}
				break;
			}
		}
	}

	public void saveToDb(WebPage webPage){
		try{
			if(dao.checkIfExisted(webPage)){
				throw new Exception();
			}
			dao.create(webPage);
			logger.info("Inserted a new record!");
		} catch (Exception e) {
			logger.warn("Catch exception: "+e.getClass());
		}
	}
//	public String getTitle(){
//		Parser parser = Parser.createParser(source.getSource(), source.getEncoding());
//		NodeVisitor visitor = new HtmlPage(parser);
//		try {
//			parser.visitAllNodesWith(visitor);
//		} catch (ParserException e) {
//			e.printStackTrace();
//		}
//		return ((HtmlPage)visitor).getTitle();
//	}
	public String getTitle(Source sc){
		int start = sc.getSource().indexOf("<title>");
		int end = sc.getSource().indexOf("</title>");
		
		if(start+end!=-2){
			return sc.getSource().substring(start+7, end);
		}
		return "";
	}
	
	public String getContent(Source sc){
		StringBean sb = new StringBean();
		try {
			//sb.setLinks(true);
			//parser.reset();
			Parser.createParser(sc.getSource(), sc.getEncoding()).visitAllNodesWith (sb);
		} catch (ParserException e) {
			e.printStackTrace();
		}		
	   return (sb.getStrings());
	}
	
//	public String getContent(){
//		return source.getSource().replaceAll("<script.*</script>", "").replaceAll("<pre.*</pre>", "");
//	}

//	public int countUrlDepth(String url){
//		String[] strArr = url.split("/");
//		return strArr.length-2;
//	}
	public int countUrlDepth(String url){
		int depth=0;
		for(int i=0;i<url.length();i++){
			if(url.charAt(i)=='/'){
				depth++;
			}
		}
		return depth-1;
	}
	public ArrayList<String> getLinks(){
		org.htmlparser.NodeFilter filter = new org.htmlparser.filters.NodeClassFilter(LinkTag.class);
		ArrayList<String> urlArr = new ArrayList<String>();
		try {
			org.htmlparser.util.NodeList list = Parser.createParser(source.getSource(), source.getEncoding()).extractAllNodesThatMatch(filter);
			for (int i = 0; i < list.size(); i++) {
                urlArr.add(((LinkTag) list.elementAt(i)).extractLink());
            }
		} catch (ParserException e) {
			logger.warn("Catch exception: "+e.getClass());
		}
		return urlArr;
	}
	
	public ArrayList<String> formAbsoluteUrl(ArrayList<String> arr){
		ArrayList<String> retArr = new ArrayList<String>();
		for(String str : arr){
			StringBuffer temp = new StringBuffer();
			try{
				if(isStartedWithDot(str)){
					continue;
				} else if (isRelativeAddr(str)){
					temp.append(source.getUrl()).append(str);
				} else {
					temp.append(str);
				}
				if(isNotStartWithHttp(temp.toString())){
					temp.insert(0, "http://");
				}
			} catch (Exception e){
				continue;
			}
			delLastSlash(temp);
			retArr.add(temp.toString());
		}
		return retArr;
	}
	public void delLastSlash(StringBuffer temp) {
		if(temp.charAt(temp.length()-1)=='/'){
			temp.deleteCharAt(temp.length()-1);
		}
	}

	private boolean isNotStartWithHttp(String str) {
		return !str.toLowerCase().contains("http://");
	}

	private boolean isStartedWithDot(String str) {
		return str.trim().charAt(0)=='.';
	}

	private boolean isRelativeAddr(String str){
		return str.trim().charAt(0)=='/';
		}
}
